/******************************************************************************
 * $Id: tifvsi.cpp 33806 2016-03-28 22:26:19Z goatbar $
 *
 * Project:  GeoTIFF Driver
 * Purpose:  Implement system hook functions for libtiff on top of CPL/VSI,
 *           including > 2GB support.  Based on tif_unix.c from libtiff
 *           distribution.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 2005, Frank Warmerdam, warmerdam@pobox.com
 * Copyright (c) 2010-2012, Even Rouault <even dot rouault at mines-paris dot org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

/*
 * TIFF Library UNIX-specific Routines.
 */
#include "cpl_vsi.h"
#include "cpl_conv.h"
#include "tifvsi.h"

// boost mongo std

// #include <string>
// #include "boost\algorithm\string.hpp"
// #include <iostream>
// #include <math.h>
// #include <cstdlib>

// using namespace std;

// #include <WinSock2.h>
// #include "mongo\client\dbclient.h"
// #include "mongo\client\gridfs.h"
// #include "mongo\bson\bson.h"

// using namespace mongo;


#include "gt_mongo.h"


#ifndef IO_CACHE_PAGE_SIZE
#define IO_CACHE_PAGE_SIZE      4096
#endif

// We avoid including xtiffio.h since it drags in the libgeotiff version
// of the VSI functions.

#ifdef RENAME_INTERNAL_LIBGEOTIFF_SYMBOLS
#include "gdal_libgeotiff_symbol_rename.h"
#endif

CPL_C_START
extern TIFF CPL_DLL * XTIFFClientOpen(const char* name, const char* mode,
                                      thandle_t thehandle,
                                      TIFFReadWriteProc, TIFFReadWriteProc,
                                      TIFFSeekProc, TIFFCloseProc,
                                      TIFFSizeProc,
                                      TIFFMapFileProc, TIFFUnmapFileProc);
CPL_C_END

typedef struct
{
	GridFS* grid;		
	char fileName[100];
	int bAtEndOfFile;	
	int64 nExpectedPos;	
} GDALMongoTiffHandle;

static tsize_t _tiffReadProc(thandle_t th, tdata_t buf, tsize_t size)
{
	GDALMongoTiffHandle* psGMTH = (GDALMongoTiffHandle*)th;
	if (psGMTH&&psGMTH->grid)
	{
		GridFile file = psGMTH->grid->findFileByName(psGMTH->fileName);
		int chunkSize = file.getChunkSize();
		int chunkIndex = floor((double)psGMTH->nExpectedPos / (double)chunkSize);
		int chunkSpan = psGMTH->nExpectedPos%chunkSize;
		tsize_t byteSize = size;
		tsize_t bufSpan = 0;
		char* startPos = (char*)buf;
		while (byteSize > 0)
		{
			GridFSChunk chunk = file.getChunk(chunkIndex);
			int len;
			const char* data = chunk.data(len);
			memcpy(startPos + bufSpan, data + chunkSpan, min(byteSize, len - chunkSpan));
			bufSpan += min(byteSize, len - chunkSpan);
			byteSize -= min(byteSize, len - chunkSpan);
            chunkSpan = 0;
			chunkIndex++;
		}

		psGMTH->nExpectedPos += size;

		return size;
	}
	return 0;
}

static tsize_t _tiffWriteProc(thandle_t th, tdata_t buf, tsize_t size)
{
    
    return 0;
}

static toff_t _tiffSeekProc(thandle_t th, toff_t off, int whence)
{
    GDALMongoTiffHandle* psGMTH = reinterpret_cast<GDALMongoTiffHandle *>(th);

	///* Optimization: if we are already at end, then no need to */
	///
	if (whence == SEEK_END)
	{
		if (psGMTH->bAtEndOfFile)
		{
			return static_cast<toff_t>(psGMTH->nExpectedPos);
		}
		psGMTH->bAtEndOfFile = TRUE;
		GridFile file = psGMTH->grid->findFileByName(psGMTH->fileName);
		psGMTH->nExpectedPos = file.getContentLength();
		return static_cast<toff_t>(psGMTH->nExpectedPos);
	}
	
	psGMTH->bAtEndOfFile = FALSE;
	psGMTH->nExpectedPos = off;

	return static_cast<toff_t>(psGMTH->nExpectedPos);
}

static int _tiffCloseProc(thandle_t th)
{
	GDALMongoTiffHandle* psGMTH = reinterpret_cast<GDALMongoTiffHandle*>(th);
	
	CPLFree(psGMTH->grid);
	CPLFree(psGMTH);
	return 0;
}

static toff_t _tiffSizeProc(thandle_t th)
{
	GDALMongoTiffHandle* psGMTH = reinterpret_cast<GDALMongoTiffHandle*>(th);
	if (psGMTH&&psGMTH->grid)
	{
        GridFile file = psGMTH->grid->findFileByName(psGMTH->fileName);
		return file.getContentLength();
	}
	return 0;
}

static int _tiffMapProc(thandle_t th, tdata_t* pbase, toff_t* psize)
{
    (void) th; (void) pbase; (void) psize;
    return (0);
}

static void _tiffUnmapProc(thandle_t th, tdata_t base, toff_t size)
{
    (void) th; (void) base; (void) size;
}

VSILFILE* VSI_TIFFGetVSILFile(thandle_t th)
{
    return NULL;
}

int VSI_TIFFFlushBufferedWrite(thandle_t th)
{
    return 0;
}

int64 VSI_TIFFGetOffset(thandle_t th)
{
	GDALMongoTiffHandle* psGMTH = reinterpret_cast<GDALMongoTiffHandle*>(th);
	if (psGMTH)
	{
		return psGMTH->nExpectedPos;
	}
	return 0;
}

void GTiffCacheOffsetOrCountMongo(thandle_t th,vsi_l_offset nBaseOffset,int nBlockId,uint32 nstrips,uint64* panVals,size_t sizeofval)
{
	int i, iStartBefore;
    vsi_l_offset nOffset, nOffsetStartPage, nOffsetEndPage;
    GByte buffer[2 * IO_CACHE_PAGE_SIZE];

    nOffset = nBaseOffset + sizeofval * nBlockId;
    nOffsetStartPage = (nOffset / IO_CACHE_PAGE_SIZE) * IO_CACHE_PAGE_SIZE;
    nOffsetEndPage = nOffsetStartPage + IO_CACHE_PAGE_SIZE;

    if( nOffset + sizeofval > nOffsetEndPage )
        nOffsetEndPage += IO_CACHE_PAGE_SIZE;
    vsi_l_offset nLastStripOffset = nBaseOffset + nstrips * sizeofval;
    if( nLastStripOffset < nOffsetEndPage )
        nOffsetEndPage  = nLastStripOffset;
    if( nOffsetStartPage >= nOffsetEndPage )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Cannot read offset/size for strile %d", nBlockId);
        panVals[nBlockId] = 0;
        return;
    }
    // liumurong
    if( _tiffSeekProc(th, nOffsetStartPage, SEEK_SET) != 0 )
    {
        panVals[nBlockId] = 0;
        return;
    }
    size_t nToRead = (size_t)(nOffsetEndPage - nOffsetStartPage);
    // liumurong
    //size_t nRead = VSIFReadL(buffer, 1, nToRead, fp);
    size_t nRead = _tiffReadProc(th,buffer,nToRead);


    if( nRead < nToRead )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Cannot read offset/size for strile around ~%d", nBlockId);
        memset(buffer + nRead, 0, nToRead - nRead);
    }
    iStartBefore = - (int)((nOffset - nOffsetStartPage) / sizeofval);
    if( nBlockId + iStartBefore < 0 )
        iStartBefore = -nBlockId;
    for(i=iStartBefore; (uint32)(nBlockId + i) < nstrips &&
        (GIntBig)nOffset + (i+1) * (int)sizeofval <= (GIntBig)nOffsetEndPage; i++)
    {
        if( sizeofval == 4 )
        {
            uint32 val;
            memcpy(&val,
                   buffer + (nOffset - nOffsetStartPage) + i * sizeof(val),
                   sizeof(val));
            panVals[nBlockId + i] = val;
        }
        else
        {
            uint64 val;
            memcpy(&val,
                   buffer + (nOffset - nOffsetStartPage) + i * sizeof(val),
                   sizeof(val));
            panVals[nBlockId + i] = val;
        }
    }
}

toff_t GTiffSeek(thandle_t th, toff_t off, int whence)
{
	return _tiffSeekProc(th,off,whence);
}

/*
 * Open a TIFF file for read/writing.
 */
TIFF* VSI_TIFFOpen(const char* name, const char* mode,
                   VSILFILE* fpL)
{
    /*
		connection str
		mongo$127.0.0.1:27017$mapserver$admin$admin$world4
	*/

	string connStr(name);
	vector<string> conns;
	boost::split(conns, connStr, boost::is_any_of("$"));
	if (conns.size() != 6||conns[0]!="mongo")		// params invalide
	{
		return NULL;
	}

	//  conn mongo
	// mongo::client::initialize();
	// mongo::DBClientConnection* mongoConn=new mongo::DBClientConnection();
	// try
	// {
	// 	mongoConn->connect(conns[1]);
	// }
	// catch (const std::exception&)
	// {
	// 	return NULL;
	// }
	// string errmsg = "auth success";
	// bool auth = mongoConn->auth(conns[2], conns[3], conns[4], errmsg);
	// if (!auth)
	// {
	// 	return NULL;
	// }

	GDALMongoConn* gconn = GDALMongo::GetInstance()->GetMongoConn(conns[1], conns[2], conns[3], conns[4]);
	if (gconn == NULL || !gconn->isConned || !gconn->isAuthed)
	{
		return NULL;
	}

	GridFS* grid=new GridFS(*gconn->conn, conns[2]);
	GridFile gfile = grid->findFileByName(conns[5]);
	if (!gfile.exists())
	{
		return NULL;
	}
	GDALMongoTiffHandle* psGMTH = static_cast<GDALMongoTiffHandle*>(CPLMalloc(sizeof(GDALMongoTiffHandle)));

	psGMTH->grid = grid;
    strcpy(psGMTH->fileName, conns[5].c_str());
	psGMTH->nExpectedPos = 0;
	psGMTH->bAtEndOfFile = FALSE;
	
	TIFF *tif =XTIFFClientOpen(name, "r",
		(thandle_t)psGMTH,
			_tiffReadProc, _tiffWriteProc,
			_tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
			_tiffMapProc, _tiffUnmapProc);
	if (tif == NULL)
		CPLFree(psGMTH);

	return tif;
}
